﻿using System;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;

namespace IInject.Reflection
{
	public abstract class MethodWrapper : ReflectionWrapper<MethodBase>
	{
		#region Constructors

		public MethodWrapper(MethodBase methodBase) : base(methodBase)
		{
			ParameterProvider = new ParameterProvider();
			Parameters = ParameterProvider.GetParameters(methodBase);
		}

		#endregion

		#region Properties

		protected ParameterProvider ParameterProvider { get; private set; }

		public ParameterCollection Parameters { get; private set; }

		public override string Name
		{
			get
			{
				return WrappedItem.Name;
			}
		}

		public override Type Type
		{
			get
			{
				return WrappedItem.DeclaringType;
			}
		}

		public bool IsAbstract
		{
			get
			{
				return WrappedItem.IsAbstract;
			}
		}

		public bool IsConstructor
		{
			get
			{
				return WrappedItem.IsConstructor;
			}
		}

		public bool IsPublic
		{
			get
			{
				return WrappedItem.IsPublic;
			}
		}

		public bool IsStatic
		{
			get
			{
				return WrappedItem.IsStatic;
			}
		}

		public bool IsVirtual
		{
			get
			{
				return WrappedItem.IsVirtual;
			}
		}

		public bool IsGenericMethod
		{
			get
			{
				return WrappedItem.IsGenericMethod;
			}
		}

		public bool IsGenericMethodDefenition
		{
			get
			{
				return WrappedItem.IsGenericMethodDefinition;
			}
		}

		#endregion

		#region Methods

		public MethodBody GetMethodBody()
		{
			return WrappedItem.GetMethodBody();
		}

		public virtual bool SetParameters(params object[] values)
		{
			if (Parameters.Count == values.Length)
			{
				for (int i = 0; i < Parameters.Count; i++)
				{
					if (values[i] == null) { continue; }

					if (Parameters[i].Type != values[i].GetType())
					{
						return false;
					}

					Parameters[i].Value = values[i];
				}

				return true;
			}

			return false;
		}

		public virtual object Invoke(object obj)
		{
			var parameters = Parameters.Select(p => p.Value).ToArray();
			return WrappedItem.Invoke(obj, parameters);
		}

		public virtual object Invoke(object obj, params object[] paramValues)
		{
			if (!SetParameters(paramValues))
			{
				return null;
			}

			return WrappedItem.Invoke(obj, paramValues);
		}

		#endregion
	}
}